gganimategganimate to
add animationsBy far, the simplest way to create visualizations with animations is
to use the gganimate
package. This effectively works as an extension to
ggplot figures but with the inclusion of various
transition_* functions
First, let’s think about when you should NOT animate
a plot. We first create a visualization of the penguins
data from before, of bill length on the y-axis against the
body mass on the x-axis colored by
species:
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ ggplot2 3.3.6 ✔ purrr 0.3.4
## ✔ tibble 3.1.8 ✔ dplyr 1.0.9
## ✔ tidyr 1.2.0 ✔ stringr 1.4.0
## ✔ readr 2.1.2 ✔ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
library(palmerpenguins)
penguins %>%
ggplot(aes(x = body_mass_g, y = bill_length_mm, color = species)) +
geom_point(alpha = 0.5, size = 2) +
labs(x = "Body Mass (g)", y = "Bill Length (mm)") +
theme_bw()
## Warning: Removed 2 rows containing missing values (geom_point).
Now, we could do the following: use the
gganimate package to only display one species
at a time with the transition_states() function:
library(gganimate)
penguins %>%
ggplot(aes(x = body_mass_g, y = bill_length_mm, color = species)) +
geom_point(alpha = 0.5, size = 2) +
labs(x = "Body Mass (g)", y = "Bill Length (mm)") +
theme_bw() +
transition_states(species,
transition_length = 0.5,
state_length = 1)
The use of transition_length and
state_length indicate how much relative time
should take place when transitioning between states and the pause at
each state, respectively. But the above use of animation is
useless!
So when should you consider using animation?
One appropriate usage is in the context of storytelling with data, to emphasize some aspect of your visual display. For instance, we’ll borrow this F1 racing dataset from Meghan Hall’s iteration of 36-315 to compare the performance of three racing teams:
# First load the data from Meghan's github
f1_data_ex <- read_csv('https://raw.githubusercontent.com/meghall06/CMU-36-315-site/main/data/constructor_pts.csv') %>%
filter(name %in% c("McLaren", "Renault", "Racing Point"),
year == 2020)
## Rows: 1260 Columns: 12
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (2): name, race_name
## dbl (9): constructorStandingsId, raceId, constructorId, points, position, p...
## date (1): date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Now display the results across the rounds:
f1_data_ex %>%
ggplot(aes(x = round, y = points, group = name, color = name)) +
geom_line(size = 2) +
scale_x_continuous(breaks = seq(1, 17, 1)) +
labs(title = "The race for third place in the 2020 F1 season",
y = "Accumulated points", x = NULL) +
theme_bw()
From above we can see the accumulated points increasing over time for
each team, with McLaren finishing better than both, Racing Point and
Renault, at the end. But we could incrementally reveal the results at
each stage emphasize the story of progression. We’re not adding another
dimension to the display, but we emphasize the intermediate results
through animation with the transition_reveal()
function:
f1_data_ex %>%
ggplot(aes(x = round, y = points, group = name, color = name)) +
geom_line(size = 2) +
scale_x_continuous(breaks = seq(1, 17, 1)) +
labs(title = "The race for third place in the 2020 F1 season",
y = "Accumulated points", x = NULL) +
theme_bw() +
# Reveal the results by round
transition_reveal(round)
## geom_path: Each group consists of only one observation. Do you need to adjust
## the group aesthetic?
## geom_path: Each group consists of only one observation. Do you need to adjust
## the group aesthetic?